package me.ingxin.android.view

import android.content.Context
import android.os.Parcel
import android.os.Parcelable
import android.util.AttributeSet
import android.util.Log
import android.view.*
import android.view.animation.Interpolator
import android.widget.FrameLayout
import android.widget.OverScroller
import androidx.annotation.IntDef
import androidx.core.view.GravityCompat
import androidx.core.view.ViewCompat
import androidx.customview.view.AbsSavedState
import me.ingxin.android.BuildConfig
import kotlin.math.*

/**
 * Created by ingxin on 2022/5/25
 */
class DrawerLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {

    @Retention(AnnotationRetention.SOURCE)
    @IntDef(STATE_IDLE, STATE_DRAGGING, STATE_SETTLING)
    private annotation class State

    @Retention(AnnotationRetention.SOURCE)
    @IntDef(
        LOCK_MODE_UNLOCKED,
        LOCK_MODE_LOCKED_CLOSED,
        LOCK_MODE_LOCKED_OPEN,
        LOCK_MODE_LOCKED_ALL
    )
    private annotation class LockMode

    private val listeners = ArrayList<DrawerListener>()
    private val touchSlop: Int
    private val maxVelocity: Float
    private val minVelocity: Float
    private val scroller: OverScroller

    /**是否处于onLayout阶段*/
    private var inLayout = false

    /**未完成onLayout*/
    private var notYetLayout = true

    /**标记是否处于手指移动*/
    private var isMoving = false

    private var lastMotionX = 0f
    private var lastMotionY = 0f
    private var initialMotionX = 0f
    private var initialMotionY = 0f

    private var velocityTracker: VelocityTracker? = null

    @LockMode
    private var lockMode = LOCK_MODE_UNLOCKED

    /**当前抽屉状态*/
    @State
    private var drawerState = STATE_IDLE

    private val locationArray = IntArray(2)

    /**Interpolator defining the animation curve for scroller*/
    private val interpolator = Interpolator {
        val t = it - 1f
        t * t * t * t * t + 1.0f
    }

    init {
        //设置焦点获取模式,child优先获取,最后parent获取
        descendantFocusability = FOCUS_AFTER_DESCENDANTS

        //最大最小速率
        val vc = ViewConfiguration.get(context)
        maxVelocity = vc.scaledMaximumFlingVelocity.toFloat()
        minVelocity = vc.scaledMinimumFlingVelocity.toFloat()
        touchSlop = vc.scaledPagingTouchSlop
        scroller = OverScroller(context, interpolator)

        //关闭多点触控
        isMotionEventSplittingEnabled = false
        isFocusableInTouchMode = true
    }


    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        inLayout = true
        val width = r - l
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (child.visibility == GONE) {
                continue
            }
            val lp = child.layoutParams as LayoutParams
            if (!isDrawerView(child)) {
                //非抽屉
                child.layout(
                    lp.leftMargin,
                    lp.topMargin,
                    lp.leftMargin + child.measuredWidth,
                    lp.topMargin + child.measuredHeight
                )
            } else {
                val childWidth = child.measuredWidth
                val childHeight = child.measuredHeight

                //根据抽屉左右类型计算水平方向布局位置
                val isLeftDrawer = isLeftDrawerViewGravity(child)
                val childLeft = if (isLeftDrawer) {
                    //为左边的drawer
                    -childWidth + (childWidth * lp.onScreen).toInt()
                } else {
                    //right
                    width - (childWidth * lp.onScreen).toInt()
                }

                //计算竖直方向位置并layout
                when (lp.gravity and Gravity.VERTICAL_GRAVITY_MASK) {
                    Gravity.BOTTOM -> {
                        val height = b - t
                        child.layout(
                            childLeft,
                            height - lp.bottomMargin - child.measuredHeight,
                            childLeft + childWidth,
                            height - lp.bottomMargin
                        )
                    }

                    Gravity.CENTER_VERTICAL -> {
                        val height = b - t
                        var childTop = (height - childHeight) / 2

                        if (childTop < lp.topMargin) {
                            childTop = lp.topMargin
                        } else if (childTop + childHeight > height - lp.bottomMargin) {
                            childTop = height - lp.bottomMargin - childHeight
                        }
                        child.layout(
                            childLeft, childTop, childLeft + childWidth,
                            childTop + childHeight
                        )
                    }

                    else -> {
                        //Gravity.TOP && else
                        child.layout(
                            childLeft, lp.topMargin, childLeft + childWidth,
                            lp.topMargin + childHeight
                        )
                    }
                }

                val newVisibility = if (lp.onScreen > 0) VISIBLE else INVISIBLE
                if (child.visibility != newVisibility) {
                    child.visibility = newVisibility
                }

                //初始化时设置了打开
                if (lp.onScreen >= 1f && lp.openState == LayoutParams.FLAG_IS_OPENED) {
                    val old = if (isLeftDrawer) {
                        -childWidth
                    } else {
                        width
                    }
                    dispatchOnDrawerSlide(1f, childLeft - old)
                }
            }
        }
        inLayout = false
        notYetLayout = false
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)

        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
            throw IllegalArgumentException(
                "DrawerLayout must be measured with MeasureSpec.EXACTLY."
            )
        }

        //设置高宽
        setMeasuredDimension(widthSize, heightSize)

        var hasDrawer = false
        val childCount = childCount
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (child.visibility == GONE) {
                continue
            }
            val lp = child.layoutParams as LayoutParams
            when {
                //测量主布局
                isContentView(child) -> {
                    // Content views get measured at exactly the layout's size.
                    val contentWidthSpec = MeasureSpec.makeMeasureSpec(
                        widthSize - lp.leftMargin - lp.rightMargin,
                        MeasureSpec.EXACTLY
                    )
                    val contentHeightSpec = MeasureSpec.makeMeasureSpec(
                        heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY
                    )
                    child.measure(contentWidthSpec, contentHeightSpec)
                }

                //测量drawer
                isDrawerView(child) -> {
                    if (hasDrawer) {
                        //只能有一个drawer
                        throw IllegalStateException("there is more than one drawer")
                    }
                    hasDrawer = true
                    val drawerWidthSpec = getChildMeasureSpec(
                        widthMeasureSpec,
                        lp.leftMargin + lp.rightMargin, lp.width
                    )
                    val drawerHeightSpec = getChildMeasureSpec(
                        heightMeasureSpec,
                        lp.topMargin + lp.bottomMargin, lp.height
                    )
                    child.measure(drawerWidthSpec, drawerHeightSpec)
                }

                else -> {
                    throw IllegalStateException(
                        ("Child " + child + " at index " + i
                                + " does not have a valid layout_gravity - must be Gravity.LEFT, "
                                + "Gravity.RIGHT or Gravity.NO_GRAVITY")
                    )
                }
            }
        }
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        val action = ev.actionMasked
        var interceptForTap = false

        trackVelocity(ev)

        when (action) {
            MotionEvent.ACTION_DOWN -> {
                val x = ev.x
                val y = ev.y
                initialMotionX = x
                initialMotionY = y
                lastMotionX = initialMotionX
                lastMotionY = initialMotionY
                isMoving = false

                //抽屉打开时需要拦截主布局上的全部事件
                if (isOpenAndOpening() && canGestureControl()) {
                    val child = findTopChildUnder(x.toInt(), y.toInt())
                    if (child != null && isContentView(child)) {
                        interceptForTap = true
                    }
                }
            }

            MotionEvent.ACTION_MOVE -> {
                val x = ev.x
                val y = ev.y
                val dx = x - lastMotionX
                val yDiff = y - initialMotionY

                val isOpen = isOpenAndOpening()
                val checkView = if (isOpen) {
                    findDrawer()
                } else {
                    findContentView()
                }

                //通过在屏幕上的坐标判断
                if (dx != 0f && canScroll(
                        checkView,
                        false,
                        dx.toInt(),
                        ev.rawX.toInt(),
                        ev.rawY.toInt()
                    )
                ) {
                    lastMotionX = x
                    lastMotionY = y
                    return false
                }

                //有效水平滑动
                if (abs(dx) > touchSlop && abs(dx) * 0.5f > abs(yDiff)) {
                    val isLeft = isLeftGravity(findDrawer())
                    if (isOpen || (dx > 0 && isLeft) || (dx < 0 && !isLeft)) {
                        lastMotionX = x
                        lastMotionY = y
                        interceptForTap = true
                    }
                }

            }

            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                //do nothing
            }
        }

        logD("onInterceptTouchEvent:action = ${action}, return $interceptForTap")

        return interceptForTap

    }

    override fun onTouchEvent(ev: MotionEvent): Boolean {
        val wantTouchEvents = true
        val action = ev.action and MotionEvent.ACTION_MASK

        trackVelocity(ev)

        when (action) {
            MotionEvent.ACTION_DOWN -> {
                val x = ev.x
                val y = ev.y
                initialMotionX = x
                initialMotionY = y
                lastMotionX = initialMotionX
                lastMotionY = initialMotionY
                isMoving = false
            }

            MotionEvent.ACTION_MOVE -> {
                val x = ev.x
                val y = ev.y
                val dx = x - lastMotionX
                val dy = y - lastMotionY

                val xDiff = x - initialMotionX
                val yDiff = y - initialMotionY

                //满足x位移大于y*系数&起始点距离大于阈值 才认定为有效水平滑动
                if (abs(dx) > 0 && abs(dx) > abs(FILTRATE_VERTICAL * dy)
                    && (xDiff * xDiff + yDiff * yDiff > touchSlop * touchSlop)
                ) {
                    isMoving = true
                }

                if (isMoving && dx != 0f && canGestureControl()) {
                    val drawerView = findDrawer()
                    updateDrawerState(STATE_DRAGGING, drawerView)
                    dragTo(drawerView, (drawerView.left + dx).toInt(), dx.toInt())
                }

                lastMotionX = x
                lastMotionY = y

            }

            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                val x = ev.x
                val y = ev.y
                if (canGestureControl()) {
                    if (isMoving) {
                        dragUp(findDrawer())
                    } else {
                        //主布局上的点击事件
                        val child = findTopChildUnder(x.toInt(), y.toInt())
                        if (child != null && isContentView(child)) {
                            closeDrawer(true)
                        }
                    }
                }
                cancelVelocityTracker()
            }
        }

        logD("onTouchEvent:action = ${action}, return $wantTouchEvents")
        return wantTouchEvents
    }


    /**
     * 通过设置的模式判断是否可以有手势控制
     */
    private fun canGestureControl(): Boolean {
        val mode = getDrawerLockMode()
        return mode == LOCK_MODE_UNLOCKED || (mode == LOCK_MODE_LOCKED_CLOSED && isOpenAndOpening())
                || (mode == LOCK_MODE_LOCKED_OPEN && !isOpenAndOpening())
    }

    /**
     * 追踪加速度
     */
    private fun trackVelocity(ev: MotionEvent) {
        if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
            cancelVelocityTracker()
        }
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain()
        }
        velocityTracker?.addMovement(ev)
    }

    private fun cancelVelocityTracker() {
        velocityTracker?.recycle()
        velocityTracker = null
    }

    override fun requestLayout() {
        if (!inLayout) {
            super.requestLayout()
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        notYetLayout = true
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        notYetLayout = true
    }

    override fun generateLayoutParams(attrs: AttributeSet?): ViewGroup.LayoutParams =
        LayoutParams(context, attrs)

    override fun generateLayoutParams(p: ViewGroup.LayoutParams?): ViewGroup.LayoutParams {
        return when (p) {
            is LayoutParams -> LayoutParams(p)
            is MarginLayoutParams -> LayoutParams(p)
            else -> LayoutParams(p)
        }
    }

    override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams = LayoutParams(
        ViewGroup.LayoutParams.MATCH_PARENT,
        ViewGroup.LayoutParams.MATCH_PARENT
    )

    override fun checkLayoutParams(p: ViewGroup.LayoutParams?): Boolean =
        p is LayoutParams && super.checkLayoutParams(p)

    /**
     * 设置保存drawer的偏移量百分比
     * @param drawerView drawer
     * @param slidePercent drawer显示的百分比
     * @param dx 本次变化的位移
     */
    private fun setDrawerViewPercent(drawerView: View, slidePercent: Float, dx: Int) {
        val lp = drawerView.layoutParams as LayoutParams
        if (slidePercent == lp.onScreen) {
            return
        }
        updateScreenPercent(lp, slidePercent)
        dispatchOnDrawerSlide(slidePercent, dx)
    }

    private fun updateScreenPercent(layoutParams: LayoutParams, slidePercent: Float) {
        layoutParams.onScreen = slidePercent
        if (slidePercent == 0f) {
            //抽屉关闭时,主布局不需要屏蔽事件
            findContentView().needIntercept = false
        } else if (slidePercent == 1f) {
            //抽屉打开时,主布局不需要响应事件,事件应该交给parent处理
            findContentView().needIntercept = true
        }
    }

    /**
     * drawer滑动事件分发.
     * @param slidePercent drawer显示的百分比
     * @param dx 本次变化的位移
     */
    private fun dispatchOnDrawerSlide(slidePercent: Float, dx: Int) {
        val listenerCount: Int = listeners.size
        for (i in listenerCount - 1 downTo 0) {
            listeners[i].onDrawerSlide(slidePercent, dx)
        }
    }

    /**
     * drawer的关闭事件分发
     */
    private fun dispatchOnDrawerClosed(drawerView: View) {
        val lp = drawerView.layoutParams as LayoutParams
        //由打开状态变成关闭状态,则通知回调
        if (lp.openState and LayoutParams.FLAG_IS_OPENED == LayoutParams.FLAG_IS_OPENED) {
            val listenerCount = listeners.size
            for (i in listenerCount - 1 downTo 0) {
                listeners[i].onDrawerClosed()
            }
        }
        lp.openState = LayoutParams.FLAG_IS_CLOSED
    }

    /**
     * drawer打开事件分发
     */
    private fun dispatchOnDrawerOpened(drawerView: View) {
        val lp = drawerView.layoutParams as LayoutParams
        //当前不是打开状态
        if (lp.openState and LayoutParams.FLAG_IS_OPENED == 0) {
            val listenerCount = listeners.size
            for (i in listenerCount - 1 downTo 0) {
                listeners[i].onDrawerOpened()
            }
        }
        lp.openState = LayoutParams.FLAG_IS_OPENED
    }

    /**
     * 当view未设置gravity被认为是content
     */
    private fun isContentView(child: View): Boolean = child is DrawerContentLayout

    private fun isDrawerView(child: View): Boolean {
        val gravity = (child.layoutParams as LayoutParams).gravity
        val absGravity =
            GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(child))
        return absGravity and Gravity.START != 0 || absGravity and Gravity.END != 0
    }

    /**
     * 检测drawer上的方向是否为左侧(left,start)
     * @param drawerView drawer
     */
    private fun isLeftDrawerViewGravity(drawerView: View): Boolean {
        val checkFor = Gravity.LEFT
        val absGravity = getDrawerViewAbsoluteGravity(drawerView)
        return absGravity and checkFor == checkFor
    }

    /**
     * 获取drawer上设置的方向
     */
    private fun getDrawerViewAbsoluteGravity(drawerView: View): Int {
        val gravity = (drawerView.layoutParams as LayoutParams).gravity
        return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this))
    }

    private fun findVisibleDrawer(): View? {
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (isDrawerView(child) && isDrawerVisible(child)) {
                return child
            }
        }
        return null
    }

    private fun findDrawer(): View {
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (isDrawerView(child)) {
                return child
            }
        }
        throw IllegalArgumentException("can not find drawer view")
    }

    private fun findContentView(): DrawerContentLayout {
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            if (isContentView(child)) {
                return child as DrawerContentLayout
            }
        }
        throw IllegalArgumentException("can not find DrawerContentLayout")
    }

    private fun isDrawerVisible(drawer: View): Boolean {
        return (drawer.layoutParams as LayoutParams).onScreen > 0
    }

    /**
     * 抽屉是否处于打开状态
     */
    fun isOpened(): Boolean {
        val openState =
            (findVisibleDrawer()?.layoutParams as? LayoutParams)?.openState ?: return false
        return (openState and LayoutParams.FLAG_IS_OPENED) == LayoutParams.FLAG_IS_OPENED
    }


    /**
     * is opened or opening
     */
    private fun isOpenAndOpening(): Boolean {
        val openState =
            (findVisibleDrawer()?.layoutParams as? LayoutParams)?.openState ?: return false
        return (openState and LayoutParams.FLAG_IS_OPENED) == LayoutParams.FLAG_IS_OPENED ||
                (openState and LayoutParams.FLAG_IS_OPENING) == LayoutParams.FLAG_IS_OPENING
    }

    /**
     * 更新保存drawer的状态值
     * @param activeState new state
     * @param activeDrawer drawer view
     */
    private fun updateDrawerState(@State activeState: Int, activeDrawer: View?) {
        //处于空闲状态时
        if (activeDrawer != null && activeState == STATE_IDLE) {
            val lp = activeDrawer.layoutParams as LayoutParams
            if (lp.onScreen == 0f) {
                dispatchOnDrawerClosed(activeDrawer)
            } else if (lp.onScreen == 1f) {
                dispatchOnDrawerOpened(activeDrawer)
            }
        }

        //更新状态
        if (activeState != drawerState) {
            drawerState = activeState
            val listenerCount = listeners.size
            for (i in listenerCount - 1 downTo 0) {
                listeners[i].onDrawerStateChanged(activeState)
            }
        }
    }

    @LockMode
    fun getDrawerLockMode(): Int = lockMode

    fun setDrawerLockMode(@LockMode lockMode: Int) {
        if (this.lockMode != lockMode) {
            this.lockMode = lockMode
            if (lockMode != LOCK_MODE_UNLOCKED) {
                cancelVelocityTracker()
            }

            if (lockMode == LOCK_MODE_LOCKED_OPEN) {
                openDrawer(findDrawer(), false)
            } else if (lockMode == LOCK_MODE_LOCKED_CLOSED) {
                closeDrawer(findDrawer(), false)
            }
        }
    }

    /**
     * 关闭抽屉.
     * @param animate 是否动画过渡
     */
    fun closeDrawer(animate: Boolean) {
        closeDrawer(findDrawer(), animate)
    }

    private fun closeDrawer(drawerView: View, animate: Boolean) {
        val lp = drawerView.layoutParams as LayoutParams
        if (lp.onScreen == 0f) {
            //已经关闭
            return
        }
        if (notYetLayout) {
            //未布局
            updateScreenPercent(lp, 0f)
            lp.openState = LayoutParams.FLAG_IS_CLOSED
        } else if (animate) {
            //或上一个正在关闭的状态
            lp.openState = lp.openState or LayoutParams.FLAG_IS_CLOSING
            if (isLeftDrawerViewGravity(drawerView)) {
                smoothSlideViewTo(drawerView, -drawerView.width, drawerView.top)
            } else {
                smoothSlideViewTo(drawerView, width, drawerView.top)
            }
        } else {
            slideViewTo(drawerView, 0f)
            updateDrawerState(STATE_IDLE, drawerView)
            drawerView.visibility = INVISIBLE
        }
    }

    private fun smoothSlideViewTo(drawerView: View, finalLeft: Int, finalTop: Int): Boolean {
        val startLeft = drawerView.left
        val startTop = drawerView.top
        val dx = finalLeft - startLeft
        val dy = finalTop - startTop
        if (dx == 0 && dy == 0) {
            // Nothing to do. Send callbacks, be done.
            scroller.abortAnimation()
            updateDrawerState(STATE_IDLE, drawerView)
            return false
        }
        val duration = computeSettleDuration(drawerView, dx, dy, 0, 0)

        if (!scroller.isFinished) {
            scroller.abortAnimation()
        }

        //启动scroller,需要配合computeScroll
        scroller.startScroll(startLeft, startTop, dx, dy, duration)
        updateDrawerState(STATE_SETTLING, drawerView)
        //通知重绘,触发computeScroll
        invalidate()
        return true
    }

    override fun computeScroll() {
        if (continueSettling()) {
            ViewCompat.postInvalidateOnAnimation(this)
        }
    }

    private fun continueSettling(): Boolean {
        if (drawerState == STATE_SETTLING) {
            val drawerView = findDrawer()

            var keepGoing = scroller.computeScrollOffset()
            val x = scroller.currX
            val dx = x - drawerView.left

            if (dx != 0) {
                //移动drawer
                dragTo(drawerView, x, dx)
            }

            if (keepGoing && x == scroller.finalX) {
                //已经到达位置了,强制停止
                scroller.abortAnimation()
                keepGoing = false
            }

            if (!keepGoing) {
                //动画已经完成
                updateDrawerState(STATE_IDLE, drawerView)
            }
        }

        return drawerState == STATE_SETTLING
    }

    private fun computeSettleDuration(drawerView: View, dx: Int, dy: Int, xv: Int, yv: Int): Int {
        val xvel = clampMag(xv.toFloat(), minVelocity, maxVelocity).toInt()
        val yvel = clampMag(yv.toFloat(), minVelocity, maxVelocity).toInt()
        val absDx = abs(dx)
        val absDy = abs(dy)
        val absXVel = abs(xvel)
        val absYVel = abs(yvel)
        val addedVel = absXVel + absYVel
        val addedDistance = absDx + absDy
        val xweight =
            if (xvel != 0) absXVel.toFloat() / addedVel else absDx.toFloat() / addedDistance
        val yweight =
            if (yvel != 0) absYVel.toFloat() / addedVel else absDy.toFloat() / addedDistance
        val xduration = computeAxisDuration(dx, xvel, drawerView.width)
        val yduration = computeAxisDuration(dy, yvel, drawerView.width)
        return (xduration * xweight + yduration * yweight).toInt()
    }

    private fun computeAxisDuration(delta: Int, v: Int, motionRange: Int): Int {
        if (delta == 0) {
            return 0
        }
        val halfWidth = width / 2
        val distanceRatio = min(1f, abs(delta).toFloat() / width)
        val distance = halfWidth + distanceInfluenceForSnapDuration(distanceRatio) * halfWidth

        val velocity = abs(v)
        val duration = if (velocity > 0) {
            4 * (1000 * abs(distance / velocity)).roundToInt()
        } else {
            val range = abs(delta).toFloat() / motionRange
            ((range + 1) * BASE_SETTLE_DURATION).toInt()
        }
        return min(duration, MAX_SETTLE_DURATION)
    }

    private fun distanceInfluenceForSnapDuration(v: Float): Float {
        // center the values about 0.
        var f = v - 0.5f
        f *= 0.3f * Math.PI.toFloat() / 2.0f
        return sin(f.toDouble()).toFloat()
    }


    /**
     * 打开抽屉.
     * @param animate 是否动画过渡
     */
    fun openDrawer(animate: Boolean) {
        openDrawer(findDrawer(), animate)
    }

    /**
     * 添加drawer事件监听
     * @param listener
     */
    fun addDrawerListener(listener: DrawerListener) {
        if (!listeners.contains(listener)) {
            listeners.add(listener)
        }
    }

    /**
     * 移除drawer事件监听
     * @param listener
     */
    fun removeDrawerListener(listener: DrawerListener) {
        listeners.remove(listener)
    }

    private fun openDrawer(drawerView: View, animate: Boolean) {
        val lp = drawerView.layoutParams as LayoutParams
        if (notYetLayout) {
            updateScreenPercent(lp, 1f)
            lp.openState = LayoutParams.FLAG_IS_OPENED
        } else if (animate) {
            lp.openState = lp.openState or LayoutParams.FLAG_IS_OPENING
            if (isLeftDrawerViewGravity(drawerView)) {
                smoothSlideViewTo(drawerView, 0, drawerView.top)
            } else {
                smoothSlideViewTo(drawerView, width - drawerView.width, drawerView.top)
            }
        } else {
            slideViewTo(drawerView, 1f)
            updateDrawerState(STATE_IDLE, drawerView)
            drawerView.visibility = VISIBLE
        }
    }

    private fun slideViewTo(drawerView: View, slideOffset: Float) {
        val drawerWidth = drawerView.width

        val isLeftDrawer = isLeftDrawerViewGravity(drawerView)
        val newLeft = if (isLeftDrawer) {
            //为左边的drawer
            -drawerWidth + (drawerWidth * slideOffset).toInt()
        } else {
            //right
            width - (drawerWidth * slideOffset).toInt()
        }


        val clampX = clampViewPositionHorizontal(drawerView, newLeft)
        val oldLeft = drawerView.left

        val dx = clampX - oldLeft

        drawerView.offsetLeftAndRight(dx)
        setDrawerViewPercent(drawerView, slideOffset, dx)
    }

    /**
     * 检测view或view内部是否有可以向dx方向滑动的view
     * @param v 需要检测的view
     * @param checkV 是否血药检测自己
     * @param dx 检测的方向
     * @param x 在屏幕坐标系的x坐标
     * @param y 在屏幕坐标系的y坐标
     */
    private fun canScroll(v: View, checkV: Boolean, dx: Int, x: Int, y: Int): Boolean {
        if (v.visibility != View.VISIBLE) {
            return false
        }

        if (v is ViewGroup) {
            val count = v.childCount
            for (i in count - 1 downTo 0) {
                val child = v.getChildAt(i)
                child.getLocationOnScreen(locationArray)
                val sx = locationArray[0]
                val sy = locationArray[1]
                if (x >= sx && x < sx + child.width && y >= sy && y < sy + child.height) {
                    if (canScroll(
                            child, true, dx, x, y
                        )
                    ) {
                        return true
                    }
                }
            }
        }
        return checkV && v.canScrollHorizontally(-dx)
    }

    /**
     * 找到指定坐标下,最上层的child,如果没有则返回null
     */
    private fun findTopChildUnder(x: Int, y: Int): View? {
        for (i in childCount - 1 downTo 0) {
            val child: View = getChildAt(i)
            if (x >= child.left && x < child.right && y >= child.top && y < child.bottom) {
                return child
            }
        }
        return null
    }

    /**
     * 拖拽抽屉.
     * @param drawerView 抽屉布局
     * @param left 目标位置的left
     * @param dx 本次x轴偏移量
     */
    private fun dragTo(drawerView: View, left: Int, dx: Int) {
        if (dx == 0) {
            return
        }

        val oldLeft = drawerView.left
        //修正边界
        val clampedX = clampViewPositionHorizontal(drawerView, left)
        val clampedDx = clampedX - oldLeft

        //位移抽屉
        ViewCompat.offsetLeftAndRight(drawerView, clampedDx)

        //计算drawer偏移百分比
        val drawerWidth = drawerView.width
        val visibleWidth = if (isLeftGravity(drawerView)) {
            drawerWidth + clampedX
        } else {
            width - clampedX
        }
        val slidePercent = visibleWidth.toFloat() / drawerWidth
        setDrawerViewPercent(drawerView, slidePercent, clampedDx)

        drawerView.visibility = if (slidePercent == 0f) INVISIBLE else VISIBLE

    }

    /**
     * 限制调整left值,防止越界
     * @param left 目标位置的left
     * @return 返回调整后的left
     */
    private fun clampViewPositionHorizontal(drawerView: View, left: Int): Int {
        return if (isLeftGravity(drawerView)) {
            max(-drawerView.width, min(left, 0))
        } else {
            max(width - drawerView.width, min(left, width))
        }
    }

    private fun isLeftGravity(view: View): Boolean {
        return if (isDrawerView(view)) {
            isLeftDrawerViewGravity(view)
        } else {
            isLeftDrawerViewGravity(findDrawer())
        }
    }


    /**
     * 拖拽移动结束.
     * @param drawerView drawerView
     */
    private fun dragUp(drawerView: View) {
        //水平速度
        var xvel = 0f
        velocityTracker?.let {
            it.computeCurrentVelocity(1000, maxVelocity)
            xvel = clampMag(it.xVelocity, minVelocity, maxVelocity)
        }

        val offset = (drawerView.layoutParams as LayoutParams).onScreen
        //触发速度系数,调节丢手后的速度
        val factor = 4

        if (isLeftDrawerViewGravity(drawerView)) {
            if (xvel > factor * minVelocity || (xvel >= 0f && offset > 0.5f)) {
                openDrawer(drawerView, true)
            } else {
                closeDrawer(drawerView, true)
            }
        } else {
            if (xvel < -(factor * minVelocity) || (xvel <= 0f && offset > 0.5f)) {
                openDrawer(drawerView, true)
            } else {
                closeDrawer(drawerView, true)
            }
        }

    }

    private fun logD(msg: String) {
        if (BuildConfig.DEBUG) {
            Log.d(TAG, msg)
        }
    }

    private fun clampMag(value: Float, absMin: Float, absMax: Float): Float {
        val absValue = abs(value)
        if (absValue < absMin) return 0f
        return if (absValue > absMax) if (value > 0) absMax else -absMax else value
    }

    override fun onRestoreInstanceState(state: Parcelable?) {
        if (state !is SavedState) {
            super.onRestoreInstanceState(state)
            return
        }

        super.onRestoreInstanceState(state.superState)

        if (state.openDrawerGravity != Gravity.NO_GRAVITY) {
            val toOpen = findDrawer()
            openDrawer(toOpen, true)
        }
        setDrawerLockMode(state.lockMode)
    }

    override fun onSaveInstanceState(): Parcelable? {
        val superState = super.onSaveInstanceState() ?: return null
        val ss = SavedState(superState)
        val childCount = childCount
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val lp = child.layoutParams as LayoutParams
            // Is the current child fully opened (that is, not closing)?
            val isOpenedAndNotClosing = lp.openState == LayoutParams.FLAG_IS_OPENED
            // Is the current child opening?
            val isClosedAndOpening = lp.openState == LayoutParams.FLAG_IS_OPENING
            if (isOpenedAndNotClosing || isClosedAndOpening) {
                // If one of the conditions above holds, save the child's gravity
                // so that we open that child during state restore.
                ss.openDrawerGravity = lp.gravity
                break
            }
        }
        ss.lockMode = lockMode
        return ss
    }

    /**
     * State persisted across instances
     */
    private class SavedState : AbsSavedState {

        var openDrawerGravity = Gravity.NO_GRAVITY

        @LockMode
        var lockMode = 0

        constructor(superState: Parcelable) : super(superState)
        constructor(source: Parcel, loader: ClassLoader?) : super(source, loader) {
            openDrawerGravity = source.readInt()
            lockMode = source.readInt()
        }

        override fun writeToParcel(parcel: Parcel, flags: Int) {
            super.writeToParcel(parcel, flags)
            parcel.writeInt(openDrawerGravity)
            parcel.writeInt(lockMode)
        }

        override fun describeContents(): Int {
            return 0
        }

        companion object CREATOR : Parcelable.ClassLoaderCreator<SavedState> {

            override fun createFromParcel(parcel: Parcel, loader: ClassLoader?): SavedState {
                return SavedState(parcel, loader)
            }

            override fun createFromParcel(parcel: Parcel): SavedState {
                return SavedState(parcel, null)
            }

            override fun newArray(size: Int): Array<SavedState?> {
                return arrayOfNulls(size)
            }
        }

    }


    class LayoutParams : MarginLayoutParams {

        var gravity = Gravity.NO_GRAVITY
            private set

        /**drawer偏移的百分比,contentView的值始终为0,只有drawer才会有值*/
        var onScreen = 0f

        /**打开状态*/
        var openState = FLAG_IS_CLOSED

        constructor(c: Context, attrs: AttributeSet?) : super(c, attrs) {
            val a = c.obtainStyledAttributes(attrs, intArrayOf(android.R.attr.layout_gravity))
            this.gravity = a.getInt(0, Gravity.NO_GRAVITY)
            a.recycle()
        }

        constructor(width: Int, height: Int) : super(width, height)
        constructor(source: MarginLayoutParams) : super(source)
        constructor(source: ViewGroup.LayoutParams?) : super(source)
        constructor(source: LayoutParams) : super(source) {
            gravity = source.gravity
        }

        companion object {

            /**已经关闭*/
            const val FLAG_IS_CLOSED = 0X0

            /**已经打开*/
            const val FLAG_IS_OPENED = 0x1

            /**正在打开中*/
            const val FLAG_IS_OPENING = 0x2

            /**正在关闭中*/
            const val FLAG_IS_CLOSING = 0x4
        }
    }

    interface DrawerListener {

        /**
         * 滑动时回调.
         * @param slidePercent 滑动的百分比
         * @param dx 本次
         */
        fun onDrawerSlide(slidePercent: Float, dx: Int)

        fun onDrawerOpened()

        fun onDrawerClosed()

        fun onDrawerStateChanged(@State newState: Int)

    }

    companion object {

        private const val TAG = "DrawerLayout"

        /**ms*/
        private const val BASE_SETTLE_DURATION = 256

        /**ms*/
        private const val MAX_SETTLE_DURATION = 600

        /**滑动过滤系数,当水平(位移*系数>竖直位移)时,才认定为需要拦截的有效水平滑动事件*/
        private const val FILTRATE_VERTICAL = 1.5f

        /**空闲*/
        const val STATE_IDLE = 0

        /**拖拽中*/
        const val STATE_DRAGGING = 1

        /**fling中*/
        const val STATE_SETTLING = 2

        /**默认模式,不锁定任何操作*/
        const val LOCK_MODE_UNLOCKED = 0

        /**关闭drawer,用户不能通过手势打开,可以通过程序控制打开*/
        const val LOCK_MODE_LOCKED_CLOSED = 1

        /**打开drawer,用户不能通过手势关闭,可以通过程序控制关闭*/
        const val LOCK_MODE_LOCKED_OPEN = 2

        /**禁用手势打开或者关闭*/
        const val LOCK_MODE_LOCKED_ALL = 3

    }

}

class DrawerContentLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {

    /**是否需要拦截事件交给parent处理*/
    internal var needIntercept = false

    override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
        if (needIntercept) {
            //将事件拦截下来,进入onTouchEvent
            return true
        }
        return super.onInterceptTouchEvent(event)
    }

    override fun onTouchEvent(event: MotionEvent?): Boolean {
        //因为通过onInterceptTouchEvent拦截了事件, onTouchEvent返回false不消费事件,会导致该事件后续touch不在分发给这view
        //通过上面的机制达到屏蔽事件,将事件交给parent处理的目的
        return false
    }

}